﻿using System.Collections.Generic;

using Vestras.StarCraft2.Luna.Parser;
using Vestras.StarCraft2.Luna.Parser.Ast;

namespace Vestras.StarCraft2.Luna.Validation {
    public sealed class ReturnStatementTypeValidator : ITypeValidator {
        public System.Type[] Type {
            get {
                return new[] { typeof(ReturnStatement) };
            }
        }

        public bool Validate(object value) {
            if (value != null && value is ReturnStatement) {
                ErrorSink.UpdateChangeCollection();
                ReturnStatement returnStatement = value as ReturnStatement;
                Function parent = returnStatement.GetLogicalParentOfType<Function>();
                if (parent.Type.GetQualifiedId() == "void" && returnStatement.ReturnValue != null) {
                    if (!ErrorSink.HasChanged()) {
                        ErrorSink.AddError(ErrorSink.Error.Create("Since '" + parent.Name + "' returns void, a return keyword must not be followed by an object expression", returnStatement.ReturnValue));
                    }

                    return false;
                }

                if (returnStatement.ReturnValue != null) {
                    if (!returnStatement.ReturnValue.IsDefaultType()) {
                        if (!ErrorSink.HasChanged()) {
                            ErrorSink.AddError(ErrorSink.Error.Create("Bulk copy is not supported.", returnStatement.ReturnValue));
                        }

                        return false;
                    }

                    if (returnStatement.ReturnValue is MemberAccessExpression || returnStatement.ReturnValue is IdentifierExpression) {
                        IdentifierExpression identifierExpression = returnStatement.ReturnValue as IdentifierExpression;
                        MemberAccessExpression memberAccessExpression = returnStatement.ReturnValue as MemberAccessExpression;
                        bool isDefaultBoolValue = identifierExpression != null && (identifierExpression.Identifier == "true" || identifierExpression.Identifier == "false");
                        if (identifierExpression != null && (identifierExpression.Identifier == "null")) {
                            string typeId = parent.Type.GetQualifiedId();
                            if (typeId == "bool" || typeId == "int" || typeId == "fixed") { // non-nullable types
                                if (!ErrorSink.HasChanged()) {
                                    ErrorSink.AddError(ErrorSink.Error.Create("Attempt to convert null to non-nullable type '" + typeId + "'", returnStatement.ReturnValue));
                                }

                                return false;
                            }
                        } else {
                            if (!isDefaultBoolValue) {
                                IList<Entity> referencedEntities = null;
                                if (identifierExpression != null) {
                                    referencedEntities = (new List<Entity>(identifierExpression.GetReferencedEntities()));
                                } else {
                                    referencedEntities = (new List<Entity>(memberAccessExpression.GetReferencedEntities()));
                                }

                                if (referencedEntities != null) {
                                    string ambiguityEntities;
                                    if (referencedEntities.Count == 0) {
                                        if (!ErrorSink.HasChanged()) {
                                            ErrorSink.AddError(ErrorSink.Error.Create("Cannot find entity '" + returnStatement.ReturnValue.GetQualifiedId() + "'", returnStatement.ReturnValue));
                                        }

                                        return false;
                                    }

                                    if (referencedEntities.IsAmbiguous(out ambiguityEntities)) {
                                        if (!ErrorSink.HasChanged()) {
                                            ErrorSink.AddError(ErrorSink.Error.Create("Ambiguity between entities " + ambiguityEntities, returnStatement.ReturnValue));
                                        }

                                        return false;
                                    }

                                    Entity referencedEntity = referencedEntities[0];
                                    if (!returnStatement.ReturnValue.DoesExpressionResolveToType(parent.Type)) {
                                        if (!ErrorSink.HasChanged()) {
                                            ErrorSink.AddError(ErrorSink.Error.Create("Cannot convert type '" + referencedEntity.GetTypeQualifiedId() + "' to type '" + parent.Type.GetQualifiedId() + "'", returnStatement.ReturnValue));
                                        }

                                        return false;
                                    }

                                    if (referencedEntity is Variable && ((Variable)referencedEntity).Value == null && !((Variable)referencedEntity).HasBeenAssigned && !((Variable)referencedEntity).IsParameter && !(((Variable)referencedEntity).Type.ArrayDimensionExpressions.Count > 0) /* Variables with array types don't need to be initialized */) {
                                        if (!ErrorSink.HasChanged()) {
                                            ErrorSink.AddError(ErrorSink.Error.Create("Use of uninitialized variable '" + returnStatement.ReturnValue.GetQualifiedId() + "'", returnStatement.ReturnValue));
                                        }

                                        return false;
                                    }
                                }
                            }
                        }
                    }
                }

                return true;
            }

            return false;
        }
    }
}
